//
//  GameScene.swift
//  TetrisGame
//
//  Created by mac on 2020/11/23.
//

import SpriteKit
import GameplayKit


/// GameScene
class GameScene: SKScene {
    
    
    //MARK: - proterty
    let gameLayer = SKNode()
    let shapeLayer = SKNode()
    let LayerPosition = CGPoint(x: (K_APP_WIDTH - BlockSize * CGFloat(NumColumns)) * 0.25, y: -(UIDevice.current.isiPhoneX() ? 24 : 0))
    
    var tick:(()->())?
    private var lastTick:Date?
    lazy var tickLengthMillis = TickLengthLevelOne
    private lazy var textureCache = Dictionary<String, SKTexture>()
    
    
    //MARK: - override
    override func update(_ currentTime: TimeInterval) {
        // Called before each frame is rendered
        
        guard let lastTick = self.lastTick else {
            return
        }

        let timePassed = lastTick.timeIntervalSinceNow * -1000.0
        if timePassed > self.tickLengthMillis {
            self.lastTick = Date.init()
            self.tick?()
        }
    }
    
    override init(size: CGSize) {
        super.init(size: size)
        
        let _anchorPoint:CGPoint = .init(x: 0, y: 1.0)
        anchorPoint = _anchorPoint
        
        let background = SKSpriteNode.init(color: K_APP_VIEWCONTROLLER_BACKGROUND_COLOR, size: size)
        background.position = .zero
        background.anchorPoint = _anchorPoint
        
        self.addChild(background)
        self.addChild(gameLayer)
        
        let gameBoardTexture = SKTexture(imageNamed: "gameboard")
        let gameBoard = SKSpriteNode(texture: gameBoardTexture,
                                     size: CGSize.init(width: BlockSize * CGFloat(NumColumns), height: BlockSize * CGFloat(NumRows)))
        gameBoard.anchorPoint = _anchorPoint
        gameBoard.position = LayerPosition

        shapeLayer.position = LayerPosition
        shapeLayer.addChild(gameBoard)
        gameLayer.addChild(shapeLayer)
        
        DispatchQueue.main.async {
            self.run(SKAction.repeatForever(SKAction.playSoundFileNamed("theme.mp3", waitForCompletion: true)))
        }
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}


//MARK: -
extension GameScene {
    
    
    /// 播放游戏声音
    /// - Parameter sound: sound description
    func playSound(sound:String) {
        DispatchQueue.main.async {
            self.run(SKAction.playSoundFileNamed(sound, waitForCompletion: false))
        }
    }
    
    func startTicking(){
        lastTick = Date.init()
    }
    
    
    func stopTicking(){
        lastTick = nil
    }
    
    
    /// 方块移动的坐标
    /// - Parameters:
    ///   - column: Int
    ///   - row: Int
    /// - Returns: CGPoint
    func pointForColumn(column: Int, row: Int) -> CGPoint {
        let x = LayerPosition.x + (CGFloat(column) * BlockSize) + (BlockSize / 2)
        let y = LayerPosition.y - ((CGFloat(row) * BlockSize) + (BlockSize / 2))
        print("{x:\(x),y:\(y)}")
        return CGPoint.init(x: x, y: y)
    }

    
    /// 添加预览方块图
    /// - Parameters:
    ///   - shape: shape description
    ///   - completion: completion description
    /// - Returns: description
    func addPreviewShapeToScene(shape:Shape, completion:@escaping () -> ()) {
         for block in shape.blocks {

             var texture = textureCache[block.spriteName]
             if texture == nil {
                 texture = SKTexture(imageNamed: block.spriteName)
                 textureCache[block.spriteName] = texture
             }
             let sprite = SKSpriteNode(texture: texture)

             sprite.position = pointForColumn(column: block.column, row:block.row - 2)
             shapeLayer.addChild(sprite)
             block.sprite = sprite

             // Animation
             sprite.alpha = 0

             let moveAction = SKAction.move(to: pointForColumn(column: block.column, row: block.row),
                                            duration: TimeInterval(0.2))
             moveAction.timingMode = .easeOut
            
             let fadeInAction = SKAction.fadeAlpha(to: 0.7, duration: 0.4)
             fadeInAction.timingMode = .easeOut
             sprite.run(SKAction.group([moveAction, fadeInAction]))
        }
        
        run(SKAction.wait(forDuration: 0.4), completion: completion)
    }

    
    /// 移除预览图
    /// - Parameters:
    ///   - shape: Shape
    ///   - completion:
    /// - Returns: 
    func movePreviewShape(shape:Shape, completion:@escaping () -> ()) {
        for block in shape.blocks {
            let sprite = block.sprite!
            let moveTo = pointForColumn(column: block.column, row:block.row)
            let moveToAction:SKAction = SKAction.move(to: moveTo, duration: 0.2)
            moveToAction.timingMode = .easeOut
            sprite.run(SKAction.group([moveToAction, SKAction.fadeAlpha(to: 1.0, duration: 0.2)]), completion: {})
        }
        run(SKAction.wait(forDuration: 0.2), completion: completion)
    }

    
    /// 方块掉落方法
    /// - Parameters:
    ///   - shape: Shape
    ///   - completion: @escaping () -> ()
    /// - Returns: 
    func redrawShape(shape:Shape, completion:@escaping () -> ()) {
         for block in shape.blocks {
            let sprite = block.sprite!
            let moveTo = pointForColumn(column: block.column, row:block.row)
            let moveToAction:SKAction = SKAction.move(to: moveTo, duration: 0.05)
            
            moveToAction.timingMode = .easeOut
            if block == shape.blocks.last {
               sprite.run(moveToAction, completion: completion)
            } else {
               sprite.run(moveToAction)
            }
         }
    }
    
    
    /// 爆炸效果
    /// - Parameters:
    ///   - linesToRemove: linesToRemove description
    ///   - fallenBlocks: fallenBlocks description
    ///   - completion: completion description
    /// - Returns: description
    func animateCollapsingLines(linesToRemove: Array<Array<Block>>, fallenBlocks: Array<Array<Block>>, completion:@escaping () -> ()) {
         var longestDuration: TimeInterval = 0
 
         for (columnIdx, column) in fallenBlocks.enumerated() {
            for (blockIdx, block) in column.enumerated() {
                 let newPosition = pointForColumn(column: block.column, row: block.row)
                 let sprite = block.sprite!

                 let delay = (TimeInterval(columnIdx) * 0.05) + (TimeInterval(blockIdx) * 0.05)
                 let duration = TimeInterval(((sprite.position.y - newPosition.y) / BlockSize) * 0.1)
                 let moveAction = SKAction.move(to: newPosition, duration: duration)
                 
                 moveAction.timingMode = .easeOut
                 sprite.run(
                     SKAction.sequence([
                                        SKAction.wait(forDuration: delay),
                         moveAction]))
                 longestDuration = max(longestDuration, duration + delay)
             }
         }

         for rowToRemove in linesToRemove {
             for block in rowToRemove {

                 let randomRadius = CGFloat(UInt(arc4random_uniform(400) + 100))
                 let goLeft = arc4random_uniform(100) % 2 == 0

                 var point = pointForColumn(column: block.column, row: block.row)
                 point = .init(x: point.x + (goLeft ? -randomRadius : randomRadius), y: point.y)

                 let randomDuration = TimeInterval(arc4random_uniform(2)) + 0.5

                 var startAngle = CGFloat(Double.pi)
                 var endAngle = startAngle * 2
                 if goLeft {
                     endAngle = startAngle
                     startAngle = 0
                 }
                
                 let archPath = UIBezierPath(arcCenter: point, radius: randomRadius, startAngle: startAngle, endAngle: endAngle, clockwise: goLeft)
                 let archAction = SKAction.follow(archPath.cgPath, asOffset: false, orientToPath: true, duration: randomDuration)
                 archAction.timingMode = .easeIn
                 let sprite = block.sprite!

                 sprite.zPosition = 100
                 sprite.run(
                     SKAction.sequence(
                        [SKAction.group([archAction, SKAction.fadeOut(withDuration: TimeInterval(randomDuration))]),
                             SKAction.removeFromParent()]))
             }
         }

         run(SKAction.wait(forDuration: longestDuration), completion:completion)
     }
}
